package nachos.threads;

import nachos.machine.Machine;

import java.util.LinkedList;
import java.util.Queue;

/**
 * communicator 允许线程同步交换32位消息
 * 可以多个线程同时等待 speak，也可以多个线程同时等待 listen
 * 但是不应该存在 speaker 和 listener 同时等待的情况，因为此时两个线程可以配对
 */
public class Communicator {
    /**
     * 分配一个通信器
     */
    public Communicator() {
        lock = new Lock();
        speakerCondition = new Condition2(lock);
        listenerCondition = new Condition2(lock);

        wordQueue = new LinkedList<>();
    }

    /**
     * 等待线程通过此通信器 listen，然后将 word 传输给 listener
     *
     * <p>
     * 直到此线程与 listener 线程配对后才会返回
     * 只有一个 listener 应该接收 word
     *
     * @param word 要传输的整数
     */
    public void speak(int word) {
        // 获得锁
        lock.acquire();

        // 将 word 存入队列
        wordQueue.offer(word);

        // 如果此时没有 listener 处于休眠状态
        if (sleepListenerCount == 0) {
            // 让 speaker 进入休眠状态，等待 listener 唤醒
            sleepSpeakerCount++;
            speakerCondition.sleep();
        } else {
            // 唤醒一个 listener
            listenerCondition.wake();
            sleepListenerCount--;
        }

        // 释放锁
        lock.release();
    }

    /**
     * 等待线程通过此通信器 speak，然后将传入到 speak() 的 word 返回
     *
     * @return 被传输的整数
     */
    public int listen() {

        // 获得锁
        lock.acquire();

        // 如果此时没有 speaker 处于休眠状态
        if (sleepSpeakerCount == 0) {
            // 让 listener 进入休眠状态，等待 speaker 唤醒
            sleepListenerCount++;
            listenerCondition.sleep();
        } else {
            // 唤醒一个 speaker
            speakerCondition.wake();
            sleepSpeakerCount--;
        }

        // 获取 word
        int word = wordQueue.poll();

        // 释放锁
        lock.release();

        return word;
    }

    // 锁和条件变量
    private Lock lock;
    private Condition2 speakerCondition;
    private Condition2 listenerCondition;

    // 存储 word 的队列
    private Queue<Integer> wordQueue;

    // 处于休眠状态的 speaker 和 listener 的数量，二者不可同时大于 0
    private int sleepSpeakerCount;
    private int sleepListenerCount;
}
